﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Giri;

namespace Giri.L1
{
    class Message
    {
        private Message higherLevelMessage;

        private byte[] data;

        protected Message(int headerLength)
        {
            higherLevelMessage = null;
            data = new byte[headerLength+4];
            BitConverter.GetBytes(headerLength).CopyTo(data, 0);
        }

        public Message(byte[] xdata)
            : this(xdata, 0)
        {
        }

        public Message(Message msg)
            : this(msg.GetMessageBytes())
        {
        }

        public Message(byte[] xdata, int begin)
        {
            data = new byte[BitConverter.ToInt32(xdata, begin)];                // Set data with the size that is given in xdata's first byte
            begin += data.Length;                                               // Move the index "pointer" to the higher level message
            if (begin < xdata.Length)                                           // Has the "pointer" reached the end of the data?
            {
                int datalen = BitConverter.ToInt32(xdata,begin);
                if(datalen != 0)
                    higherLevelMessage = new Message(xdata, begin, datalen);    // No, set the higher level message
            }
        }

        private Message(byte[] xdata, int begin, int length)
        {
            data = new byte[length];                                            // Set data with the size that is given in xdata's first byte
            begin += data.Length;                                               // Move the index "pointer" to the higher level message
            if (begin < xdata.Length)                                           // Has the "pointer" reached the end of the data?
            {
                int datalen = BitConverter.ToInt32(xdata,begin);
                if(datalen != 0)
                    higherLevelMessage = new Message(xdata, begin, datalen);    // No, set the higher level message
            }
        }

        public byte[] GetMessageBytes()
        {
            // Get the higher level message (if any)
            byte[] msg = (higherLevelMessage != null) ?             // Does the message have a higher level message?
                        higherLevelMessage.GetMessageBytes() :      // Yes, use the data of the message
                        new byte[0];                                // No, use empty array of bytes

            byte[] result = new byte[data.Length+msg.Length];       // Create the result array
            data.CopyTo(result, 0);                                 // Copy this message's data to result
            msg.CopyTo(result, data.Length);                        // Append the higher level data (if any) to the message.

            return result;
        }

        protected void setHigherLevelMessage(Message msg)
        {
            higherLevelMessage = msg;
        }

        protected void Data(int pos, byte b)
        {
            data[pos + 4] = b;
        }

        protected byte Data(int pos)
        {
            return data[pos + 4];
        }

        protected byte[] Data()
        {
            return data;
        }

        protected void Data(int pos, byte[] data)
        {
            data.CopyTo(this.data, pos + 4);
        }

        protected void Data(byte[] data)
        {
            this.data = new byte[data.Length + 4];
            BitConverter.GetBytes(data.Length).CopyTo(this.data,0);
            data.CopyTo(this.data, 4);
        }

        protected void Resize(int newSize)
        {
            ChangeSize(newSize - data.Length);
        }

        protected void ChangeSize(int diff)
        {
            if (diff > 0)
                Lengthen(diff);
            else if (diff < 0)
                Shorten(-diff);
        }

        protected void Lengthen(int sizeToAdd)
        {
            byte[] temp = new byte[data.Length];
            data.CopyTo(temp, 0);
            data = new byte[data.Length + sizeToAdd];
            temp.CopyTo(data, 0);
        }

        protected void Shorten(int sizeToReduce)
        {
            byte[] temp = new byte[data.Length];
            data.CopyTo(temp, 0);
            data = new byte[data.Length - sizeToReduce];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = temp[i];
            }
        }

        protected int Length()
        {
            return data.Length;
        }
    }
}
